home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 25
/
Cream of the Crop 25.iso
/
program
/
vgatx10s.zip
/
VGATEXT.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1997-04-12
|
19KB
|
591 lines
/*
* This file is part of the VgaText C++ Programming Library
*
* Copyright (c) 1995, 1997 by Branislav L. Slantchev
* A fine product of Silicon Creations, Inc. (gargoyle)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the License which accompanies this
* software. This library is distributed in the hope that it will
* be useful, but without any warranty; without even the implied
* warranty of merchantability or fitness for a particular purpose.
*
* You should have received a copy of the License along with this
* library, in the file LICENSE.DOC; if not, write to the address
* below to receive a copy via electronic mail.
*
* You can reach Branislav L. Slantchev (Silicon Creations, Inc.)
* at bslantch@cs.angelo.edu. The file SUPPORT.DOC has the current
* telephone numbers and the postal address for contacts.
*/
#include "vgatext.h"
#include "comdef.h"
/*
* v g a t e x t m o d e e f f e c t c l a s s
* ──────────────────────────────────────────────────────────────────────────
* this module implements the special vga text mode effect class
*/
#ifndef INCLUDED_ASSERT_H
#define INCLUDED_ASSERT_H
#include <assert.h>
#endif
#ifndef INCLUDED_DOS_H
#define INCLUDED_DOS_H
#include <dos.h>
#endif
#ifndef INCLUDED_STRING_H
#define INCLUDED_STRING_H
#include <string.h>
#endif
#undef disable
#undef enable
#define TIMER_INTERRUPT 8
// used by the Grade??? routines and the grade calculation function
static struct{
char increment; // value to add to the color value
char fixupRange; // range of palettes than need the 'fixup' added
char fixup; // fixup (either 1 or -1) that is sometimes needed
} redScale, greenScale, blueScale;
static union REGS regs;
static struct SREGS sreg;
#define VideoInt() int86(0x10, ®s, ®s)
#define xVideoInt() int86x(0x10, ®s, ®s, &sreg)
Boolean zVgaText::vgaCard = False;
Boolean zVgaText::initVga = False;
Boolean zVgaText::restoreBlink = False;
Boolean zVgaText::timerSet = False;
void interrupt (*zVgaText::timerVector)(...) = 0;
uchar zVgaText::origDac[768];
uchar zVgaText::origPal[17];
uchar zVgaText::origPageMode;
uchar zVgaText::origDispPage;
uchar zVgaText::pal[17];
uchar zVgaText::dac[768];
ushort zVgaText::delayTicks = 1;
ushort zVgaText::countdown = 9;
char zVgaText::delta = 1;
char zVgaText::nCurPalette = 0;
/*
* wait for a top of vertical retrace (must call before switching palettes)
* not used in this module. since we are using int 10h to do the work, we
* don't need to sync with the retrace (the interrupt handler does that)
*/
#if 0
static void
wait_vertical_retrace()
{
/*
while( (inportb (0x3da) & 8) )
;
while( !(inportb (0x3da) & 8) )
;
*/
_asm mov dx,0x3da
VRT:
_asm in al,dx
_asm test al,8
_asm jnz VRT
NoVRT:
_asm in al,dx
_asm test al,8
_asm jz NoVRT
}
#endif
zVgaText::zVgaText()
{
assert(False == initVga);
if( False != (vgaCard = Detect()) ) resume();
}
zVgaText::~zVgaText()
{
suspend();
initVga = False;
}
void
zVgaText::resume()
{
// save the 16 VGA palette registers plus the border color
regs.x.ax = 0x1009;
sreg.es = FP_SEG((uchar far *)&origPal[0]);
regs.x.dx = FP_OFF((uchar far *)&origPal[0]);
xVideoInt();
// save the original DAC rgb registers (all 256 of them)
regs.x.ax = 0x1017;
regs.x.bx = 0;
regs.x.cx = 256;
sreg.es = FP_SEG((uchar far *)&origDac[0]);
regs.x.dx = FP_OFF((uchar far *)&origDac[0]);
xVideoInt();
// save the original paging mode and display page
regs.x.ax = 0x101A;
VideoInt();
origPageMode = regs.h.bl;
origDispPage = regs.h.bh;
// set our own user palette registers. we use the values from 0 to 15
// to make it easier to map attributes directly for DAC palettes. we
// also just copy the current border color from the original setting
for( int i = 0; i < 16; ++i ) pal[i] = (uchar)i;
pal[16] = origPal[16];
regs.x.ax = 0x1002;
sreg.es = FP_SEG((uchar far *)&pal[0]);
regs.x.dx = FP_OFF((uchar far *)&pal[0]);
xVideoInt();
// set our own video DAC rgb palettes (16 of them, 16 colors each, with
// 3 bytes per color (RGB).
ResetPalettes();
LoadPalettes();
// set the paging mode to 1 (DAC is treated as 16 palettes of 16 colors)
regs.x.ax = 0x1013;
regs.x.bx = 0x0100;
VideoInt();
// set some initial values here
delayTicks = 1;
delta = 1;
}
// suspend the timer and restore the settings
void
zVgaText::suspend()
{
// restore the original timer handler
disable();
// restore the original 16 vga registers and border color
regs.x.ax = 0x1002;
sreg.es = FP_SEG((uchar far *)&origPal[0]);
regs.x.dx = FP_OFF((uchar far *)&origPal[0]);
xVideoInt();
// restore all 256 DAC rgb registers
regs.x.ax = 0x1012;
regs.x.bx = 0;
regs.x.cx = 256;
sreg.es = FP_SEG((uchar far *)&origDac[0]);
regs.x.dx = FP_OFF((uchar far *)&origDac[0]);
xVideoInt();
// restore the paging mode
regs.x.ax = 0x1013;
regs.h.bl = 0;
regs.h.bh = origPageMode;
VideoInt();
// restore the display page
regs.x.ax = 0x1013;
regs.h.bl = 1;
regs.h.bh = origDispPage;
VideoInt();
// check to see if we need to restore the blinking
if( restoreBlink )
{
regs.x.ax = 0x1003;
regs.h.bl = 1;
VideoInt();
restoreBlink = False;
}
}
// reset the user DAC rgb definition in all 16 palettes to original. this
// does NOT place the color definition into the VGA DAC. note that all
// 0x33 values should actually be 0x3F to match the default palette set up
// by the BIOS. thse look a little dimmer, but they do leave some room
// for special affects like pulsing and fading for those too...
void
zVgaText::ResetPalettes()
{
static const char cpOrigDac[48] =
"\x00\x00\x00" // black
"\x00\x00\x2A" // blue
"\x00\x2A\x00" // green
"\x00\x2A\x2A" // cyan
"\x2A\x00\x00" // red
"\x2A\x00\x2A" // magenta
"\x2A\x15\x00" // brown (close enough)
"\x2A\x2A\x2A" // light gray
"\x15\x15\x15" // dark gray (close enough)
"\x00\x00\x3F" // light blue
"\x00\x3F\x00" // light green
"\x00\x3F\x3F" // light cyan
"\x3F\x00\x00" // light red
"\x3F\x00\x3F" // light magenta
"\x3F\x3F\x00" // yellow
"\x3F\x3F\x3F"; // white
for( int i = 0; i < 16; ++i ) memcpy(dac + (i*48), cpOrigDac, 48);
nCurPalette = 0;
}
// load all 16 palettes into the VGA
void
zVgaText::LoadPalettes()
{
regs.x.ax = 0x1012;
regs.x.bx = 0;
regs.x.cx = 256;
sreg.es = FP_SEG((uchar far *)&dac[0]);
regs.x.dx = FP_OFF((uchar far *)&dac[0]);
xVideoInt();
}
// change the mode between blinking and 4-bit backgrounds
void
zVgaText::EnableBlink(Boolean enable)
{
regs.x.ax = 0x1003;
// if we are enabling blinking, clear the flag for the restore
if( enable )
{
regs.h.bl = 1;
restoreBlink = False;
}
// if we are enabling 4-bit backgrounds, set the blink restore flag
else
{
regs.h.bl = 0;
restoreBlink = True;
}
VideoInt();
}
// enable the timer intercept handler. this is the routine that cycles
// through the 16 custom palettes. only works if the timer is installed.
void
zVgaText::enable()
{
if( !timerSet )
{
timerSet = True;
countdown = 1;
nCurPalette = 0;
timerVector = getvect(TIMER_INTERRUPT);
setvect(TIMER_INTERRUPT, timerHandler);
}
}
// disable the timer intercept handler. this will freeze the palette at
// the argument given by the 'nPalette' parameter (between 0 and 15)
void
zVgaText::disable(char nPalette)
{
if( timerSet )
{
setvect(TIMER_INTERRUPT, timerVector);
regs.x.ax = 0x1013;
regs.h.bl = 1;
regs.h.bh = nPalette & 0x0F; // just to make sure
VideoInt();
timerSet = False;
}
}
// get a pointer to the RGB definition of the 'nAttrib' attribute in the
// user DAC palette 'nPalette'. both values can range from 0 to 15.
uchar*
zVgaText::GetDacPtr(char attrib, char palette)
{
int nColor, nPalette;
// make sure we stay within the limits
attrib &= 0x0F;
palette &= 0x0F;
// map the attribute into the VGA registers. since we initialize
// those to their indexes, they drop out of the picture and the
// attribute will map directly to the same color. since the user
// may have modified those, we need the step anyway (and just to
// be safe, make sure the user hasn't put large numbers there..)
nColor = pal[attrib] & 0x0F;
// since each color has 3-byte RGB definitions, multiply the
// resulting color number by 3 to get to the correct byte offset
nColor = nColor * 3;
// the DAC palette itself consists of 16 palettes, with 16 colors
// each. and each color has 3-byte RGB definitions. to get the the
// offset for the palette, we multiply the palette number by 48
// which is the size of each individual palette (16 colors * 3 bytes)
nPalette = palette * 48;
// the offset into the palette for the requested attribute will be
// the nColor value we already calculated, and the pointer returned
// is to the position within the user DAC palette (nPalette + nColor)
return &dac[nPalette + nColor];
}
// sets the number of ticks to skip before a palette change occurs in the
// interrupt handler. this will also reset the current setting. this
// routine will work even if the timer is not enabled (saves the setting)
void
zVgaText::SetDelay(ushort nTicks)
{
delayTicks = nTicks;
countdown = delayTicks;
}
// set the delta skip step: this is the number of palettes that the timer
// intercept will skip when a palette change is required. this also makes
// sure that the current palette is a multiple of delta
void
zVgaText::SetDelta(char nDelta)
{
delta = nDelta & 0x0F; // stay within the limits
nCurPalette = (nCurPalette / delta) * delta; // fix the palette number
}
// create a pulsing color definition. this is color which changes its
// intensity over the palettes. this routine will not modify palette 0,
// but all the ones following it. 'aDelta' is the value to add at each step
// and 'nAttrib' is the color attribute that we are modifying
void
zVgaText::PulseColor(char nAttrib, char aDelta)
{
PulseColor(nAttrib, aDelta, 0, 15);
}
// this is the actual routine which creates the pulsing definitions. note
// that nAttrib should be between 0 and 15 and the end palette should be
// larger than the start. the start palette is not modified. this routine
// does not load the palettes into the vga DAC registers (use LoadPalettes)
void
zVgaText::PulseColor(char nAttrib, char aDelta, char nStart, char nEnd)
{
if( nEnd > 0 && nEnd < 16 && nEnd > nStart )
{
uchar *dacptr = GetDacPtr(nAttrib, nStart);
while( ++nStart <= nEnd )
{
// get the values from the current palette
uchar r = dacptr[0], g = dacptr[1], b = dacptr[2];
// get to the next palette by adding the size of the
// palette to the pointer to the attrib in the current one
// the size of each palette is 48 bytes, so this is enough
dacptr += 48;
// increment non-zero primaries only, make sure we stay within
// the maximum limits (63 is the maximum color saturation)
if( 0 != dacptr[0] ) dacptr[0] = min(r + aDelta, 0x3F);
if( 0 != dacptr[1] ) dacptr[1] = min(g + aDelta, 0x3F);
if( 0 != dacptr[2] ) dacptr[2] = min(b + aDelta, 0x3F);
}
}
}
// set the RGB color definition for all 16 palettes (see SetColor() below)
void
zVgaText::SetColor(char nAttrib, char rgb[3])
{
SetColor(nAttrib, rgb, 0, 15);
}
// the actual routine which sets the color definition for a range of
// palettes. this function sets the color for attribute 'nAttrib' to
// the RGB values in the 'aRGB' parameter. the input is verified, the
// attribute is forced within limits. if the requested range is invalid,
// nothing is done. the new palettes are not sent to the vga DAC registers
void
zVgaText::SetColor(char nAttrib, char aRGB[3], char nStart, char nEnd)
{
if( nEnd > 0 && nEnd < 16 && nStart <= nEnd )
{
uchar *dacptr = GetDacPtr(nAttrib, nStart);
while( nStart++ <= nEnd )
{
// set the color definition to the RGB values, make sure
// that we stay within the maximum color saturation allowed
dacptr[0] = aRGB[0] & 0x3F;
dacptr[1] = aRGB[1] & 0x3F;
dacptr[2] = aRGB[2] & 0x3F;
dacptr += 48; // get to the next palette's color definition
}
}
}
// grade a color definition starting with the RGB values in palette 0
// and ending with the 'aRGB' values in palette 15 (all palettes are used)
// the first palette is not modified and the new values are not loaded
// into the vga DAC registers (use LoadPalettes() to do that)
void
zVgaText::GradeColor(char nAttrib, char aRGB[3])
{
GradeColor(nAttrib, aRGB, 0, 15);
}
// grades a color definition starting with the RGB values in the nStart
// palette and slowly progressing towards the 'aRGB' values over the
// range of palettes between nStart and nEnd. this causes the color to
// gradually change between the starting and end values. the first palette
// is not modified and the new palettes are not loaded into the vga DAC
void
zVgaText::GradeColor(char nAttrib, char aRGB[3], char nStart, char nEnd)
{
if( 0 < nEnd && nEnd < 16 && nEnd > nStart )
{
// calculate the three color grade values that will change the
// starting RGB into the ending RGB over a range of palettes
uchar *dacptr = GetDacPtr(nAttrib, nStart);
CalcScale((char *)dacptr, aRGB, nEnd - nStart);
for( int i = 0; i <= nEnd - nStart; ++i )
{
uchar r = dacptr[0], g = dacptr[1], b = dacptr[2];
dacptr += 48; // get to the next palette's color definition
// add the increments and check if we need to use the fixup
dacptr[0] = r + redScale.increment;
if( i < redScale.fixupRange ) dacptr[0] += redScale.fixup;
dacptr[1] = g + greenScale.increment;
if( i < greenScale.fixupRange ) dacptr[1] += greenScale.fixup;
dacptr[2] = b + blueScale.increment;
if( i < blueScale.fixupRange ) dacptr[2] += blueScale.fixup;
}
}
}
// calculate the color gradation values. each gradation is computed so
// that the original value can gradually change into the ending value
// over a range of registers. to do that, we calculate the increment that
// will get us there. since sometimes the increment will not actually
// quite fit, it might be necessary to add/subtract a fixup value. we
// distribute this value as 1 or -1 over a number of registers. the total
// fixup will never exceed the register range which is <16 anyway, so we
// can do that. uses the global ???Scale structures to put the values
void
zVgaText::CalcScale(char startRGB[3], char endRGB[3], char nRange)
{
int delta;
// find the difference between the two color values
delta = endRGB[0] - startRGB[0];
// find the increment value over the range
redScale.increment = delta / nRange;
// find the difference after the icrement values are added up
redScale.fixupRange = delta % nRange;
// if we are decrementing the color value, set the fixup value
// to a negative increment and set the fixup range to positive
if( 0 > redScale.fixupRange )
{
redScale.fixupRange = -redScale.fixupRange;
redScale.fixup = -1;
}
else redScale.fixup = 1;
delta = endRGB[1] - startRGB[1];
greenScale.increment = delta / nRange;
greenScale.fixupRange = delta % nRange;
if( 0 > greenScale.fixupRange )
{
greenScale.fixupRange = -greenScale.fixupRange;
greenScale.fixup = -1;
}
else greenScale.fixup = 1;
delta = endRGB[2] - startRGB[2];
blueScale.increment = delta / nRange;
blueScale.fixupRange = delta % nRange;
if( 0 > blueScale.fixupRange )
{
blueScale.fixupRange = -blueScale.fixupRange;
blueScale.fixup = -1;
}
else blueScale.fixup = 1;
}
// software-simulated blinking. this one works with an 8-bit attribute
// (unlike the other routines which work with 4-bit attributes [0..15])
// it uses bits 0..3 for the foreground and bits 4..7 for the background
// attribute. the RGB color definition for the BG attribute is placed into
// palettes 8-15 for the FG attribute. since palettes 0-7 are not altered
// you can still use them for other special effects with the "foreground"
// the new palette is not sent to the vga DAC registers (use LoadPalettes)
void
zVgaText::BlinkSim(char nAttrib)
{
// get the RGB definition for the BG attribute from palette 0
uchar *bg = GetDacPtr((nAttrib >> 4) & 0x0F, 0);
// set the color attribute for the FG, from palettes 8-15
SetColor(nAttrib & 0x0F, (char *)bg, 8, 15);
}
// tries to detect a vga card
Boolean
zVgaText::Detect()
{
regs.x.ax = 0x1A00;
VideoInt();
return Boolean( regs.h.bl > 6 && regs.h.al == 0x1A);
}
// this fades out the whole palette from its current setup to black.
// it will return immediately after fading out all colors. this does
// modify the vga DAC registers and it does do the palette switching
// you must get rid of the timer intercept for this to work [disable()]
void
zVgaText::FadeOut(char aDelay)
{
int i;
// set up the graded fade out for all the attributes
for( i = 0; i < 16; ++i ) GradeColor(i, "\x00\x00\x00");
LoadPalettes();
for( i = 0; i < 16; ++i )
{
regs.x.ax = 0x1013;
regs.h.bl = 1;
regs.h.bh = (char)i;
VideoInt();
delay((unsigned)aDelay);
}
}
// fades in the palettes. this uses palette 0 as the base palette, which
// is copied to the last one. then the first palette is set to all black
// and the black is scaled to fit the final settings
void
zVgaText::FadeIn(char aDelay)
{
int i;
memset(dac, 0, 48); // set the first palette to all black
for( i = 0; i < 16; ++i ) GradeColor(i, (char *)&dac[720 + (i*3)]);
LoadPalettes();
for( i = 0; i < 16; ++i )
{
regs.x.ax = 0x1013;
regs.h.bl = 1;
regs.h.bh = (char)i;
VideoInt();
delay((unsigned)aDelay);
}
}
// this is the timer handler that is called by INT9 (each clock tick)
// this works only when installed. it will not switch palettes if the
// countdown is not 0. otherwise, it will add the 'delta' value to the
// current palette number and adjust it if necessary (if it goes beyond
// the limits of the palette numbers)
void interrupt
zVgaText::timerHandler(...)
{
countdown--;
if( 0 == countdown )
{
countdown = delayTicks;
nCurPalette += delta;
if( nCurPalette < 0 || nCurPalette > 15 )
{
delta = -delta;
nCurPalette += delta;
nCurPalette += delta;
}
_asm mov ax, 0x1013
_asm mov bl, 1
// _asm mov bh, nCurPalette
_BH = nCurPalette;
_asm int 0x10;
}
timerVector(); // chain the old handler
}
#undef TIMER_INTERRUPT
#undef VideoInt
#undef xVideoInt